home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / Hsoi's App Shell Source / HASMain.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-28  |  24.7 KB  |  794 lines  |  [TEXT/CWIE]

  1. /*
  2.     Hsoi's App Shell ©1995-1997 John C. Daub.  All Rights Reserved.
  3.     
  4.     CONTACT INFO:        Internet:          <mailto:hsoi@eden.com>
  5.                                         <http://www.eden.com/~hsoi/>
  6.  
  7.     Hsoi's App Shell (HAS) is a mix of an application skeleton and demo app.  It can be
  8.     used as the basis for the creation of a Macintosh application, or could just
  9.     be a "library" of code for "neat" things.
  10.     
  11.     My philosophy with HAS has been to both be a teaching/learning resource,
  12.     a code repository, and perhaps a place to make the lives of programmers easier.
  13.     
  14.     On the first part, I remember how it was for me when i started to program and
  15.     program Macs.  It wasn't impossible, but I did run into a lot of problems and
  16.     obstacles:  sparse, difficult to understand, or nonexistant documentation; lack
  17.     of sample code; sample code in a language I didn't understand; sample code
  18.     that was out of date; things no longer supported by their authors; no one wanting
  19.     to answer my questions; no one able to answer my questions; etc...
  20.     
  21.     So, from that, I wanted to see if I could try to make the lives of future
  22.     Mac programmers a little bit easier by hoping to save them from going through
  23.     the same problems I had to go through.
  24.     
  25.     On the second part, I would find sample code for things, but many times they'd
  26.     be in a language I didn't use (like in Pascal or C++), or the code would be
  27.     old and out of date (especially in trying to write working code for PowerPC
  28.     Macs, to take advantage of latest technologies, or whatever), or whatever
  29.     reason.  I just am trying to put a lot of stuff all in one place...it's
  30.     easier when you can just find things in one place instead of having to look
  31.     all over creation.  With HAS, you'll still have to look in other places to
  32.     find things, but I at least hope to be able to give you exact locations of
  33.     things (like URLs) to make your search easier.
  34.     
  35.     And lastly, I just hope to make things easier, not only in all of the above,
  36.     but also in the pocketbook.  I learned a lot from great books by Scott Knaster
  37.     and Dave Mark, and I highly recommend their books...the more you can learn
  38.     and the more you can know, the more you can do.  But I know what it's like
  39.     to be poor, and to be a software developer, it can get to be a costly undertaking.
  40.     
  41.     So, by providing this as freeware, I hope that it can make someone's life
  42.     a bit easier :)
  43.     
  44.     Now...more about HAS....
  45.     
  46.     HAS is System 7 dependant.  I could have put in stuff to allow backwards
  47.     compatability to System 6 (at least), but i did not want to do this since
  48.     previous system software usage is becoming more and more scarce, plus there
  49.     are many features that HAS tries to demostrate that just don't exist (or
  50.     can't exist) under anything less than System 7.
  51.     
  52.     HAS also tries to exploit many of these things in System 7 (MacOS 7) and
  53.     is working towards being as compliant as possible with Copland ("System 8"
  54.     or MacOS 8) and all the latest developments for the MacOS.  Basically,
  55.     I'm trying to help people keep up to date with the latest and greatest
  56.     things so that any software you're currently developing will have as
  57.     long of a self-life as possible.
  58.     
  59.     On that note, one nice thing about HAS over books is that as new things
  60.     come out, I can immediately update the shell to reflect those new changes
  61.     and features.  Books will always be a great thing, but for a book to
  62.     reflect a new direction Apple wishes to go in, that will take some time
  63.     for a new edition/version to come out.  Idealisticly speaking, I could
  64.     get a demonstration of a new technology into HAS in as much time as it
  65.     takes me to learn the new technology and write some samples for it.
  66.     
  67.     I first started to write it with THINK C 7.0.4, then moved to CodeWarrior 4,
  68.     and up through CW8.  It was worked on generating 68k code, and then compiled
  69.     with the PPC compiler to obtain native PowerMac code.  I have worked to try to have
  70.     the app work as nicely as possible with CodeWarrior, but compilation with 
  71.     THINK C and MPW I have not worked on, but probably will in the future.
  72.     
  73.     Developed on a Macintosh IIvx with 8 megs of RAM (and RamDoubler), 80 meg
  74.     internal hard drive, 127 meg, 540 meg, and 1 gig external hard drives (all from APS),
  75.     MacsBug 6.5.2, CodeWarrior8, EvenBetterBusError, DisposeResource, DoubleTrouble,
  76.     ZoneRanger, and various other toys...
  77.  
  78.     What do YOU need to use HAS?  A MacOS based computer (be it a Mac from Apple
  79.     or a clone...now there's something strange to say...clone) with at least
  80.     a 68020 processor or PowerPC processor (HAS programs and HAS itself needs
  81.     at least a 68020 or PPC chip, and at least System 7...and on the note of
  82.     System software version, the later the better, like 7.5.x would be great,
  83.     but for many things like drag and drop, scripting, etc...you can have at least
  84.     7.1 and acquire various system extensions from <ftp://ftp.info.apple.com/> )
  85.     The more the RAM the merrier, but I feel comfortable with 8 megs of real
  86.     RAM and using RamDoubler.  Disk space...up to your project, but hopefully
  87.     the HAS distribution will not take up more than 10 megs (with compiled
  88.     binaries, object code, etc) os disk space.  And finally, a lot of patience,
  89.     perserverance (sp?), and if you're into it, caffiene and nicotine. ;)
  90.  
  91.     Portions of this code are obviously not mine.  WASTE is copyright © 1993-1996
  92.     Marco Piovanelli; WASTE Object Handler's by Michael Kamprath; MovableModal
  93.     Library by Marco Piovanelli; WETabs also by Marco.
  94.     
  95.     I've also based some of HAS on the code of others, then did a lot of
  96.     modifying and changing to that code.  This start code came from:
  97.     Show_help by James W. Walker; Scott Knaster and Keith Rollin's book,
  98.     "Macintosh Programming Secrets"; Gary Little and Tim Swihart's, "Programming
  99.     for System 7"; John Norstad's NewsWatcher; Tom Bender's Tex-Edit+; 
  100.     Metrowerks sample code; and some stuff was taken from Apple DTS sample code (and heavily
  101.     modified and rewritten to work with the shell and MacOS progress/developments)
  102.     and various other Apple things (like Inside Macintosh, etc.).  Of course,
  103.     various help from the readers of the WASTE mailing list, the readers of
  104.     comp.sys.mac.programmer.help
  105.     
  106.     If I have forgotten anyone (person, company, etc.) in this "rights"
  107.     notice, I do apologize.  I am not trying to take credit for your work, and
  108.     if I did forget you, please contact me and we'll remedy the situation
  109.     with due haste.
  110.     
  111.     Also, HAS was originally based upon the WASTE Demo App code written by Marco
  112.     Piovanelli.  So if you see a lot of similarities between HAS and the WEDemo,
  113.     this is why.  I cannot thank Marco enough for all his support, generousity, help,
  114.     and kind words.  I owe much to Marco.
  115.     
  116.     For more detailed legal information about HAS and using it, please see the
  117.     "HAS Legal Info" file that you should have received with this distribution.  If
  118.     you do not have access to this file, please write to me and I'll send you
  119.     a copy.
  120. */
  121.  
  122.  
  123. /*    HASMain.c from Hsoi's App Shell.  ©1995-1996 John C. Daub.  All rights reserved
  124.  
  125.     This file contains the main() function, plus the stuff necessary to start
  126.     up the application (gestalt checks, intializations, etc...those things
  127.     necessary to do before we get to the main event loop).
  128.     
  129.     Additionally, it contains the main event loop (why not..where else would
  130.     I put it), and all those things necessary to the quitting of the program
  131.     (clean ups, etc).
  132. */
  133.  
  134. #pragma mark ••• #includes •••
  135.  
  136. #ifndef _WASTE_
  137. #include "WASTE.h"
  138. #endif
  139. #include "HASGlobals.h"
  140. #ifndef __HSOIS_APP_SHELL__
  141. #include "HASMain.h"
  142. #endif
  143. #include "HASDialogs.h"
  144. #include "HASMenus.h"
  145. #include "HASUtilDialogs.h"
  146. #include "HASUtilCursors.h"
  147. #include "HASMenuWindows.h"
  148. #include "HASAppleEvents.h"
  149. #include "HASEventDispatch.h"
  150. #include "HASMiscEvents.h"
  151. #include "HASWindows.h"
  152. #include "HASDragNDrop.h"
  153. #include "HASUtilities.h"
  154. #include "HASPrinting.h"
  155. #include "HASPreferences.h"
  156. #include "HASSoundSpeech.h"
  157. #ifndef _WASTEOBJECTS_
  158. #include "WASTE_Objects.h"
  159. #endif
  160. #ifndef __SPEECH__
  161. #include <Speech.h>
  162. #endif
  163.  
  164. #pragma mark -
  165. #pragma mark ••• globals •••
  166.  
  167. // some static UPP's for our drag manager stuff
  168.  
  169. static DragTrackingHandlerUPP        sTrackingHandlerUPP;
  170. static DragReceiveHandlerUPP        sReceiveHandlerUPP;
  171.         
  172.  
  173. #pragma mark -
  174.  
  175. //    Procedures and functions
  176.  
  177. /*
  178.  *    main()...where it all starts and ends!  pretty simple eh?
  179.  */
  180.  
  181. void    main( void )
  182. {
  183.     // initalize everything
  184.     
  185.     HsoiInitialize();
  186.         
  187.     // and here we go!  HsoiEventLoop() handles it all...
  188.     
  189.     HsoiEventLoop();
  190.  
  191.     ExitToShell();  // trust me....
  192.     
  193.     return;
  194. }
  195.  
  196.  
  197. /*
  198.  *    Here we do all the initialization stuff...get all the managers set up, configure stuff
  199.  *    etc etc...
  200.  */
  201.  
  202. void HsoiInitialize( void )
  203. {
  204.     short                    i = 0;
  205.     long                    scrapResult = 0;
  206.     PScrapStuff                scrapReturn = 0;
  207.     Boolean                    alreadySpoken;
  208.     OSErr                    err;
  209.     
  210.     // start out by expanding the application heap zone...
  211.     
  212.     MaxApplZone();
  213.         
  214.     // get us some more master pointers...
  215.     
  216.     for ( i = 1; i <= 20; i++ )
  217.         MoreMasters();            //    can't hurt to have more handles...who knows :)
  218.     
  219.     // call all the standard macintosh toolbox initialization functions
  220.     
  221.     InitGraf((Ptr) &qd.thePort);
  222.     InitFonts();
  223.     InitWindows();
  224.     InitMenus();
  225.     FlushEvents(everyEvent,nil);
  226.     TEInit();    // tho we use WASTE for text stuff, dialogs, etc all use TextEdit,
  227.                 // so don't remove this!
  228.     InitDialogs(nil);
  229.     InitCursor();
  230.  
  231.     //    let's make sure we can run on this computer b4 we go any further
  232.     
  233.     HsoiCheckForKosherSystem();    
  234.  
  235.     // get this app/shell's resource fork id
  236.     
  237.     gAppResourceFork = CurResFile();
  238.  
  239.     // get our cursors set up
  240.         
  241.     HsoiSetUpCursors();
  242.     
  243.     // set to our wait cursor while we take care of the rest of the startup stuff.
  244.     
  245.     SetCursor( *gWaitCursor );
  246.  
  247.     // take care of the preferences (read them in or create if necessary)
  248.     
  249.     HsoiInitPrefs();
  250.     
  251.     // init our speech stuff
  252.     
  253.     HsoiInitSpeechStuff();
  254.         
  255.     //    if desk scrap is too large, unload it
  256.     
  257.     scrapReturn = InfoScrap();
  258.     if ( scrapReturn->scrapSize > kScrapThreshold )
  259.         scrapResult = UnloadScrap();
  260.  
  261.     // make sure some of our important global variables are set to the proper values
  262.         
  263.     gInBackground = false;
  264.     gQuitting = false;
  265.     gCursorRgn = NewRgn();    //    (forces cursor-move event right away)
  266.     
  267.     // initialize Edition Manager
  268.     
  269.     err = InitEditionPack();
  270.     if ( err != noErr )
  271.         HsoiDoError( rErrorStrings, errFailedEditionInit, err, kErrDeath );
  272.     
  273.     //    install AppleEvent Handlers
  274.     
  275.     HsoiDoAEInstallation();
  276.  
  277.     //    install Text Services Manager...to support double-byte script systems
  278.     
  279.     if ( gHasTextServices )
  280.     {
  281.         err = InitTSMAwareApplication();
  282.         if ( err != noErr )
  283.             HsoiDoError( rErrorStrings, errFailedTSMInit, err, kErrDeath );
  284.     }
  285.  
  286.     //    install default drag handlers
  287.  
  288.     if ( gHasDragAndDrop )
  289.     {
  290.         sTrackingHandlerUPP = NewDragTrackingHandlerProc(HsoiMyTrackingHandler);
  291.         sReceiveHandlerUPP = NewDragReceiveHandlerProc(HsoiMyReceiveHandler);
  292.         
  293.         err = InstallTrackingHandler( sTrackingHandlerUPP, nil, nil );
  294.         if ( err != noErr )
  295.             HsoiDoError( rErrorStrings, errFailDragTrackInit, err, kErrDeath );
  296.             
  297.         err = InstallReceiveHandler( sReceiveHandlerUPP, nil, nil );
  298.         if ( err != noErr )
  299.             HsoiDoError( rErrorStrings, errFailDragReceiveInit, err, kErrDeath );
  300.     }
  301.  
  302.  
  303.     // initialize the handlers to handle WASTE objects: sounds, PICTs, files
  304.     // this is all from Michael Kamprath's WASTE Object Handlers
  305.     
  306.     err = InstallAllWASTEObjHandlers(nil);    
  307.     if ( err != noErr )
  308.         HsoiDoError( rErrorStrings, errFailObjectInit, err, kErrDeath );
  309.     
  310.     InitWASTEObjectHandlers();
  311.     
  312.     // initialize the printing routines
  313.     
  314.     err = HsoiInitPrintingStuff();
  315.     // and we should check for the error here....
  316.     
  317.     // set our standard dialog filter callback procedure into a global UPP
  318.     
  319.     HsoiGetMyStandardDialogFilter();
  320.     
  321.     // and just to be cute before we kick into things, let's say something for the
  322.     // nice users...
  323.     
  324.     if ( gMyPrefs.doSplashScreen )
  325.         alreadySpoken = HsoiDoSplashScreen();
  326.     else
  327.         alreadySpoken = false;
  328.             
  329.     if ( gHasSpeechManager && gMyPrefs.doStartupSpeech && !alreadySpoken )
  330.     {
  331.         SpeakString( "\pWelcome to Hsoi's App Shell." );
  332.         while ( SpeechBusy() != nil )
  333.             ; // do nothing, just wait until it's done speaking
  334.     }
  335.     
  336.     // set up the menus
  337.     
  338.     HsoiSetUpMenus();
  339.     
  340.     // get our Windows menu UPP's set up
  341.     
  342.     HsoiInitMenuWindowUPPs();
  343.  
  344.     // make sure we don't have any weird cursors
  345.     
  346.     InitCursor();
  347.  
  348.     return;
  349.     
  350. }
  351.  
  352. /*
  353.  *    This function will check to make sure that we have a computer that we can work
  354.  *    with...if the computer lacks something, we'll have to make ammends...
  355.  */
  356.  
  357. void    HsoiCheckForKosherSystem( void )
  358. {
  359.     OSErr        err = noErr;
  360.     long        reply = 0;
  361.     SysEnvRec    gMac;
  362.     
  363.     
  364.     //    first, let's see if we have a computer that we can use.
  365.     
  366.     //    This function checks to see if the machine is at least a MacPlus.
  367.         
  368.     //    Frankly, this might be pointless...I'd assume that if this is running on a computer
  369.     //    running less than system 7 and/or on a system less than a MacPlus, this call might
  370.     //    fail with an unimplimented trap error...in fact, a lot of this kosher system checking
  371.     //    might fail for that reason if not on a machine fitting these, but i dunno since
  372.     //    I don't have such a computer to test this all on.
  373.     
  374.  
  375.     err = SysEnvirons( curSysEnvVers, &gMac );
  376.     
  377.     if ( (err < 0 ) || (gMac.machineType < 0) ) // -2 is Mac XL/Lisa, -1 is original Mac with 64k ROMs
  378.         HsoiDoError( rErrorStrings, errWimpyROMs, gMac.machineType, kErrDeath );
  379.         
  380.     //    and now check for System 7 with Gestalt.  This will fail if Gestalt doesn't
  381.     //    exist and/or if System 7 isn't the system on the machine
  382.  
  383.     if ( !HsoiSystem7Available() )
  384.     {
  385.         HsoiDoError( rErrorStrings, errWimpySystem, gMac.systemVersion, kErrDeath );
  386.         gQuitting = true;
  387.         return;
  388.     }
  389.     
  390.     //    The next check is to make sure that WaitNextEvent() is implimented on the
  391.     //    machine.  System 7 (and hopefully future systems) should always have
  392.     //    WaitNextEvent, but just in case...read somewhere that it's good to be
  393.     //    a little redundant
  394.  
  395.     
  396.     if ( !HsoiTrapAvailable( _WaitNextEvent ) )
  397.         HsoiDoError( rErrorStrings, errWeirdSystem, gMac.systemVersion, kErrDeath );
  398.  
  399.     // let's check for AppleEvents
  400.     
  401.     err = Gestalt( gestaltAppleEventsAttr, &reply );
  402.     if ( !(( err == noErr ) && ( BTST( reply, gestaltAppleEventsPresent ))) )
  403.         HsoiDoError( rErrorStrings, errNoAppleEvents, err, kErrDeath );
  404.  
  405.     //    do we have ColorQuickDraw on this machine??
  406.     
  407.     err = Gestalt( gestaltQuickdrawVersion, &reply );
  408.     gHasColorQD = !( err || ( reply < gestalt8BitQD ) );
  409.     if ( err || !gHasColorQD )
  410.         HsoiDoError( rErrorStrings, errNoColorQD, err, kErrDeath );
  411.  
  412.     //    Again, being redundant.  Making sure the System 7 FSSpec file handler calls exist
  413.     
  414.     // check for FSspec records
  415.     
  416.     err = Gestalt( gestaltFSAttr, &reply );
  417.     if ( !(( err == noErr ) && ( BTST( reply, gestaltHasFSSpecCalls))) )
  418.         HsoiDoError( rErrorStrings, errNoFSSpec, err, kErrDeath );
  419.     
  420.     // check for StandardGetFile() and StandardPutFile()
  421.     
  422.     err = Gestalt( gestaltFSAttr, &reply );
  423.     if ( !(( err == noErr) && ( BTST( reply, gestaltStandardFile58))) )
  424.         HsoiDoError( rErrorStrings, errNoFSSpec, err, kErrDeath );
  425.     
  426.     // make sure we can use the FindFolder() function
  427.     
  428.     err = Gestalt( gestaltFindFolderAttr, &reply );
  429.     if ( !(( err == noErr) && ( BTST(reply, gestaltFindFolderPresent ))) )
  430.         HsoiDoError( rErrorStrings, errNoFSSpec, err, kErrDeath );
  431.     
  432.     // see if we have the Alias Manager present
  433.     
  434.     err = Gestalt( gestaltAliasMgrAttr, &reply );
  435.     if ( !(( err == noErr ) && ( BTST( reply, gestaltAliasMgrPresent ))) )
  436.         HsoiDoError( rErrorStrings, errNoAliasMgr, err, kErrDeath );
  437.     
  438.     //    see if we have the Edition Manager present
  439.     
  440.     err = Gestalt( gestaltEditionMgrAttr, &reply );
  441.  
  442.     // since i don't have the Edition Manager (Publish and Subscribe) stuff written
  443.     // yet (later release), we don't need to check for an error right now
  444.     
  445. //    if ( !((err == noErr) && ( BTST( reply, gestaltEditionMgrPresent ))) )
  446. //        HsoiDoError( rErrorStrings, errNoEditionManager, err, kErrDeath );
  447.     
  448.     // check for the notification manager
  449.     
  450.     err = Gestalt( gestaltNotificationMgrAttr, &reply );
  451.     if ( !((err == noErr ) && ( BTST(reply, gestaltNotificationPresent ))) )
  452.     {
  453.         HsoiDoError( rErrorStrings, errNoNotification, err, kErrDeath );
  454.         gHasNotification = false;
  455.     }
  456.     else
  457.         gHasNotification = true;
  458.     
  459.     //    determine whether or not the Drag Manager is available
  460.     
  461.     gHasDragAndDrop = ( (Gestalt( gestaltDragMgrAttr, &reply ) == noErr ) &&
  462.                         BTST( reply, gestaltDragMgrPresent ) );
  463.     
  464. #if GENERATINGCFM
  465.     // additional check needed if DragLib is weak-linked
  466.     gHasDragAndDrop = gHasDragAndDrop && (&NewDrag != nil);
  467. #endif
  468.     
  469.     // unless it's vital to your application that you have drag and drop installed,
  470.     //    don't bother calling DeathAlert()...as of this writing, having drag and drop
  471.     //    is not vital to an app's existance
  472.  
  473. //    if ( !gHasDragAndDrop )
  474. //        HsoiDoError( rErrorStrings, errNoDragAndDrop, 0, kErrDeath );
  475.  
  476.     //    determine whether or not the Text Services Manager is available
  477.     
  478.     gHasTextServices = ( Gestalt( gestaltTSMgrVersion, &reply ) == noErr );
  479.     
  480.     //    again, unless it's vital to your app's existance to have the TSM stuff, don't
  481.     //    call DeathAlert...as of this writing, it's not vital, mostly cause for me
  482.     //    I am not using a double-byte script system
  483.     
  484. //    if ( !gHasTextServices )
  485. //        HsoiDoError( rErrorStrings, errNoTextServices, 0, kErrDeath );
  486.  
  487.  
  488.     //    see if they have a sound input device (so they can record sounds)
  489.     //    this gestalt check taken right from Michael Kamprath's WASTE Object Handlers
  490.     //    in his CreateNewSoundObject() routine (we'll need to check this for HsoiAdjustMenus())
  491.     
  492.     err = Gestalt( gestaltSoundAttr, &reply );
  493.     if ( (err == noErr) && ( BTST(reply,gestaltHasSoundInputDevice)) )
  494.         gCanRecordSound = true;
  495.     else
  496.         gCanRecordSound = false;
  497.     
  498.     // it's not vital to have a sound input device (still can play sounds), but if it's
  499.     // vital to you, you might want to insert an error handler in here.  e.g.:
  500.     
  501.     // if ( !gCanRecordSound )
  502.     //    HsoiDoError( rErrorStrings, errCantRecordSounds, err, kErrDeath );
  503.     
  504.  
  505.     //    now to see if we have the Speech Manager present.  Hsoi's App Shell as
  506.     //    a whole isn't dependant upon the existance of the Speech Manager, but some
  507.     //    functionality is, so let's make sure we got it
  508.         
  509.     err = Gestalt( gestaltSpeechAttr, &reply );
  510.     if ( (err == noErr ) && ( BTST(reply, gestaltSpeechMgrPresent) ) )
  511.         gHasSpeechManager = true;
  512.     else
  513.         gHasSpeechManager = false;
  514.  
  515.     // and if it's vital for you to have speech stuff, you can uncomment this error checker
  516.     
  517.     // if ( !gHasSpeechManager )
  518.     //        HsoiDoError( rErrorStrings, errNoSpeechManager, err, kErrDeath );
  519.     
  520.     // check to see if the new ColorPicker is present
  521.     
  522.     err = Gestalt( gestaltColorPicker, &reply );
  523.     if (err == noErr)
  524.         gHasColorPicker = true;
  525.     else
  526.         gHasColorPicker = false;
  527.         
  528.     // not necessary to have this (no fatal error handling)...if no color picker, we'll
  529.     // just call GetColor instead of PickColor
  530.     
  531.     
  532.     // and we should probably be checking for more neat-o things the app is dependant
  533.     // upon, but that's left as an exercise for you :)
  534.     // besides, the majority of these things are System 7 dependant (ya got System 7, ya
  535.     // got such-and-such).  a lot of this is just redundant to be sure.
  536.     
  537.     return;
  538.     
  539. }
  540.  
  541.  
  542.  
  543. /*    HsoiEventLoop()...the main meat of all this...
  544.  
  545.     Much of this was taken directly from "Macintosh Programming Secrets" 2nd ed. by
  546.     Scott Knaster and Keith Rollin from their example in Chap. 4, "Dialogs".
  547. */
  548.  
  549. void    HsoiEventLoop( void )
  550. {
  551.  
  552.     Boolean        gotEvent = false;
  553.     EventRecord    event;
  554.     Boolean        passToDialog = false;
  555.     DialogRef    theDialog = nil;
  556.     short        itemHit = 0;
  557.     Boolean        itemWasHit = false;
  558.     WindowRef    window = nil;
  559.     
  560.     
  561.     while( !gQuitting )
  562.     {
  563.         // if we got a periodic task, we'll need to hog the processor, else give it up
  564.         // to other running applications (at least the Finder)
  565.         
  566.         gSleepTime = ( gPeriodicTask ? 1 : MAXLONG );
  567.         
  568.         // the ever popular meat of everything...getting the event!
  569.         
  570.         gotEvent = WaitNextEvent( everyEvent, &event, gSleepTime, gCursorRgn );
  571.  
  572.         //    give text services a chance to intercept this event
  573.         
  574.         if ( gHasTextServices )
  575.             TSMEvent( &event );
  576.  
  577.         //    adjust cursor shape and set mouse region
  578.         //    (we assume event->where is the current mouse position in global corrdinates
  579.         //    if event->what <= osEvt; high-level events store the event ID there
  580.         
  581.         if ( event.what <= osEvt )
  582.             HsoiAdjustCursor( event.where, gCursorRgn ); 
  583.         
  584.         //    This routine must be called periodically (ideally in the event loop).
  585.         //    It checks for and deals with any tasks that objects might need
  586.         //     (such as checking for and deallocating unneeded sound buffers) - Michael Kamprath
  587.  
  588.         window = gFirstWindow;
  589.         while ( window )
  590.         {
  591.             DoObjectTasks( HsoiGetWindowWE(window) );
  592.             window = (WindowRef)HsoiGetNextWindow(window);
  593.         }
  594.     
  595.         //    This dialog stuff is to handle the modeless dialog, (and also the movable
  596.         //    modal progree bar (the "Doing some long task..dum dee dum" one)).  It handles
  597.         //    events to these dialogs.  Doesn't really handle things like <Return>
  598.         //    but it works.  It's from Knaster's MPS book.
  599.  
  600.                 
  601.         passToDialog = true;
  602.         if ( ( FrontWindow() == nil )
  603.             || ( event.what == diskEvt )
  604.             || ( ( event.what == keyDown ) && ( ( event.modifiers & cmdKey ) != 0 )))
  605.                 passToDialog = false;
  606.         
  607.         // if there's a keyDown with the cmdKey, usually we want to reject it, however
  608.         // if it's a cmd-. on the movablemodal dialog, let's take it so we can dismiss
  609.         // the dialog with a cmd-.   yea i know...messy
  610.         
  611.         if ( (!passToDialog) && ((event.what == keyDown) || event.what == autoKey) &&
  612.              (event.modifiers & cmdKey) && ((event.message & charCodeMask) == '.') &&
  613.              (FrontWindow() == (WindowRef)gMovableModalDialog) )
  614.         {
  615.              passToDialog = true;
  616.         }
  617.         
  618.         // and it doesn't get any prettier...not only are we checking for certain
  619.         // events for a cmd-., but also we have to take the notification manager
  620.         // into account...
  621.         
  622.         if ( passToDialog )
  623.         {
  624.             if ( IsDialogEvent ( &event ) )
  625.             {
  626.                 itemWasHit = DialogSelect( &event, &theDialog, &itemHit );
  627.                 
  628.                 // check for cmd-. on the movablemodal progress dialog
  629.                 
  630.                 if ( ((event.what == keyDown) || (event.what == autoKey)) && 
  631.                       (event.modifiers & cmdKey) &&
  632.                       ((event.message & charCodeMask) == '.' ) &&
  633.                       (theDialog == gMovableModalDialog) )
  634.                 {
  635.                     itemWasHit = true; // fake a hit on the cancel button
  636.                     itemHit = ok; // and say it was on the cancel (ok) button
  637.                 }
  638.                 
  639.                 if ( itemWasHit )
  640.                 {
  641.                     HsoiDoDialogHit( theDialog, itemHit );
  642.                 }
  643.                 
  644.                 // if we have the movable modal progress dialog up, we need it to intercept suspend/resume
  645.                 // events so that the Notification Manager stuff can get posted correctly
  646.                 
  647.                 if ( gHasNotification )
  648.                 {
  649.                     if ( ( event.what == osEvt ) && ( ((event.message >> 24) & 0x0FF ) == suspendResumeMessage ) &&
  650.                             gInModalState && gPeriodicTask )
  651.                     {
  652.                         gotEvent = true;
  653.                     }
  654.                     else
  655.                         gotEvent = false;
  656.                 }
  657.             }
  658.         }
  659.  
  660.         
  661.         //    dispatch the events    
  662.         
  663.         if ( gotEvent )
  664.         {
  665.             HsoiDoEvent( &event );
  666.             gSleepTime = 0;    // force early idle after non-idle event (?)
  667.         }
  668.         else
  669.             HsoiDoIdle();
  670.         
  671.         // take care of any periodic tasks...
  672.             
  673.         if ( gPeriodicTask )
  674.             HsoiDoMainLoopTasks();
  675.         
  676.         // if we're speaking, run the hiliter
  677.         
  678.         if ( gHiliting )
  679.             HsoiRunHiliter( &event );
  680.             
  681.     }
  682.     
  683.     return;
  684. }
  685.  
  686.  
  687. /*    HsoiDoMainLoopTasks() takes care of handling things like working on a repeated job, i.e.
  688.     a progress indicator */
  689.     
  690. void    HsoiDoMainLoopTasks( void )
  691. {    
  692.     if ( gPeriodicTask )
  693.     {
  694.     
  695.         RotateCursor( TickCount() );    // keep our watch animating/spinning
  696.         
  697.         gPeriodicTask = !HsoiSetProgressDelta( 1 );
  698.         if ( !gPeriodicTask )
  699.             {
  700.                 // if we finish and the app is in the background, let's use the
  701.                 // Notification Manager to alert the user that it's all done
  702.                 
  703.                 if ( gHasNotification && gInBackground )
  704.                 {
  705.                     HsoiPostNMMessage();
  706.                 }
  707.                 
  708.                 // just hide the dialog
  709.                 
  710.                 HideWindow( GetDialogWindow(gMovableModalDialog) );
  711.                 
  712.                 // reset our global cause we're no longer in a modal state
  713.                 
  714.                 gInModalState = false;
  715.                 
  716.                 // get back to an arrow
  717.                 
  718.                 HsoiStopManualSpinning();
  719.                 
  720.                 // and make our menus look cool
  721.                 
  722.                 HsoiAdjustMenus();
  723.             }
  724.     }
  725.  
  726.     return;
  727. }
  728.  
  729.  
  730. /*
  731.     Called when the user selects Quit (or cmd-Q).
  732.  
  733.     Takes care of making sure we can quit the application all nice and clean.
  734. */
  735.  
  736. OSErr    HsoiCleanUp( SavingOption saving )
  737. {
  738.     WindowRef        window = nil;
  739.     OSErr            err = noErr;
  740.         
  741.     
  742.     // close all the windows and ask if they want to save the contents (if needed)
  743.     
  744.     do
  745.     {
  746.         window = FrontWindow();
  747.         if ( window )
  748.         {
  749.             err = HsoiDoClose( closingApplication, saving, window );
  750.             if ( err != noErr )
  751.                 return err;
  752.         }
  753.     } while ( window );
  754.     
  755.     // the user didn't cancel for any reason, so we must want to quit
  756.     
  757.     gQuitting = true;
  758.     
  759.     //    remove drag handlers
  760.     
  761.     if ( gHasDragAndDrop )
  762.     {
  763.         RemoveTrackingHandler( sTrackingHandlerUPP, nil );
  764.         RemoveReceiveHandler( sReceiveHandlerUPP, nil );
  765.     }
  766.     
  767.     // clean up object handlers
  768.     
  769.     ExitWASTEObjectHandlers();
  770.  
  771.     // dispose of the standard dialog filter proc (not really necessary, but we'll do
  772.     // it anyways.
  773.     
  774.     DisposeRoutineDescriptor( gMyStdDlogFilterProc );
  775.  
  776.     // if we have a speech channel, dump it
  777.     
  778.     if ( gSpeechOn )
  779.         DisposeSpeechChannel( gSpeechChannel );
  780.  
  781.     //    notify text services that we're closing down
  782.     
  783.     if ( gHasTextServices )
  784.         CloseTSMAwareApplication();
  785.  
  786.     // write out the prefs file
  787.  
  788.     if ( gWritePrefs )
  789.         HsoiWritePrefs();
  790.     
  791.     return err;
  792. }
  793.  
  794.